#pragma once
#include <iostream>
#include <stdarg.h>
#include <functional>
#include <unordered_map>
#include "looper.hpp"
#include "util.hpp"
#include "level.hpp"
#include "buffer.hpp"
#include "sink.hpp"
#include "formatter.hpp"
#include "message.hpp"

namespace mylog{
    class Logger{
    public:
        using ptr = std::shared_ptr<Logger>;
        enum class Type{
            LOGGER_SYNC = 0,
            LOGGER_ASYNC
        };
    protected:
        std::mutex _mtx;
        std::string _name;
        Formatter::ptr _format;
        LogLevel::value _level;
        std::vector<LogSink::ptr> _sinks;
    protected:
        bool ShouldLog(LogLevel::value level){
            return level > _level;
        }
        void Log(const LogLevel::value& level, const std::string filename, const size_t line, const char* fmt, va_list ap){
            char *buf;
            std::string msg;
            int len = vasprintf(&buf, fmt, ap);
            if(len < 0){
                msg = "格式化日志消息失败!";
            }else{
                msg.assign(buf, len);
                free(buf); // 防止内存泄漏
            }
            LogMsg lm(_name, level, filename, line, msg);
            std::stringstream ss;
            _format->Format(ss, lm);
            LogInit(std::move(ss.str()));
        }   
        virtual void LogInit(const std::string& msg) = 0;
    public:
        Logger(const std::string& name, const Formatter::ptr& format, std::vector<LogSink::ptr> sink, LogLevel::value level = LogLevel::value::DEBUG):
        _name(name), _format(format), _sinks(sink.begin(), sink.end()), _level(level){}
        std::string LoggerName(){
            return _name;
        }
        LogLevel::value LoggerValue(){
            return _level;
        }
        void Debug(const std::string filename, const size_t line, const char* fmt, ...){
            if(ShouldLog(LogLevel::value::DEBUG) == false){ return; }
            va_list ap;
            va_start(ap, fmt);
            Log(LogLevel::value::DEBUG, filename, line, fmt, ap);
            va_end(ap);
        }
        void Info(const std::string filename, const size_t line, const char* fmt, ...){
            if(ShouldLog(LogLevel::value::INFO) == false){ return; }
            va_list ap;
            va_start(ap, fmt);
            Log(LogLevel::value::DEBUG, filename, line, fmt, ap);
            va_end(ap);
        }
        void Warning(const std::string filename, const size_t line, const char* fmt, ...){
            if(ShouldLog(LogLevel::value::WARNING) == false){ return; }
            va_list ap;
            va_start(ap, fmt);
            Log(LogLevel::value::DEBUG, filename, line, fmt, ap);
            va_end(ap);
        }   
        void Error(const std::string filename, const size_t line, const char* fmt, ...){
            if(ShouldLog(LogLevel::value::ERROR) == false){ return; }
            va_list ap;
            va_start(ap, fmt);
            Log(LogLevel::value::DEBUG, filename, line, fmt, ap);
            va_end(ap);
        }
        void Fatal(const std::string filename, const size_t line, const char* fmt, ...){
            if(ShouldLog(LogLevel::value::FATAL) == false){ return; }
            va_list ap;
            va_start(ap, fmt);
            Log(LogLevel::value::DEBUG, filename, line, fmt, ap);
            va_end(ap);
        }

    public:
        class Builder{
        protected:
            Logger::Type _type;
            std::string _name;
            Formatter::ptr _format;
            LogLevel::value _level;
            std::vector<LogSink::ptr> _sinks;
        public:
            using ptr = std::shared_ptr<Builder>;
            Builder():_type(Logger::Type::LOGGER_SYNC), _level(LogLevel::value::DEBUG){}
            void SetType(const Logger::Type type) { _type = type; }
            void SetName(const std::string& name) { _name = name; }
            void SetLevel(const LogLevel::value& level) { _level = level; }
            void SetFormatter(Formatter::ptr format) { _format = format; }
            void SetFormatter(const std::string& format) { _format = std::make_shared<Formatter>(format); }
            void SetSinks(std::vector<LogSink::ptr> sinks) { _sinks.swap(sinks); }
            template<class SinkType, class ...Args>
            void SetSinks(Args &&...args){
                _sinks.push_back(SkinFactory::Build<SinkType>(std::forward<Args>(args)...));
            }
        };
    };

    class SyncLogger : public Logger{
    public:
        SyncLogger(const std::string& name, const Formatter::ptr& format, std::vector<LogSink::ptr> sink, LogLevel::value level = LogLevel::value::DEBUG):
        Logger(name, format, sink, level){
            std::cout << LogLevel::toString(level) << " 同步日志器: " << name << "创建成功...\n";
        }

        void LogInit(const std::string& msg){
            std::unique_lock<std::mutex> lock(_mtx);
            for(auto& item : _sinks){
                item->Log(msg.c_str(), msg.size());
            }
        }
    };

    class ASyncLogger : public Logger{
    protected:
        AsyncLooper::ptr _looper;
    protected:
        void BackendLogIt(Buffer &buf){ 
            std::unique_lock<std::mutex> lock(_mtx);
            for(auto& item : _sinks){
                item->Log(buf.Begin(), buf.ReadAbleSize());
            }
        }
    public:
        ASyncLogger(const std::string& name, const Formatter::ptr& format, std::vector<LogSink::ptr> sinks, LogLevel::value level = LogLevel::value::DEBUG):
        Logger(name, format, sinks, level), _looper(std::make_shared<AsyncLooper>(std::bind(&ASyncLogger::BackendLogIt, this, std::placeholders::_1))){
            std::cout << LogLevel::toString(level) << " 异步日志器: " << name << "创建成功...\n";
        }
        void LogInit(const std::string& msg){
            std::unique_lock<std::mutex> lock(_mtx);
            _looper->Push(msg);
        }
    };

    class LocalLoggerBuilder : public Logger::Builder{
    public:
        using ptr = std::shared_ptr<LocalLoggerBuilder>;
        Logger::ptr Build(){
            if(_name.empty()){
                std::cout << "日志器名不能为空!\n";
                abort();
            }
            if(_format.get() == nullptr){
                std::cout << "当前日志器：" << _name << " 未检测到日志格式，默认设置为[ %d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%n ]!\n";
                _format = std::make_shared<Formatter>();
            }
            if(_sinks.empty()){
                std::cout << "当前日志器：" << _name << " 未检测到落地方向，默认设置为标准输出!\n";
                _sinks.push_back(SkinFactory::Build<StdSink>());
            }
            if(_type == Logger::Type::LOGGER_SYNC){
                return std::make_shared<SyncLogger>(_name, _format, _sinks, _level);
            }else{
                return std::make_shared<ASyncLogger>(_name, _format, _sinks, _level);
            }
        }
    };

    class LoggerManager{
    private:
        Logger::ptr _root;
        std::unordered_map<std::string, Logger::ptr> _table;
        std::mutex _mtx;
    private:
        LoggerManager(){
            LocalLoggerBuilder::ptr builder = std::make_shared<LocalLoggerBuilder>();
            builder->SetName("root");
            _root = builder->Build();
            _table.insert(std::make_pair("root", _root));
        }
        LoggerManager(const LoggerManager&) = delete;
    public:
        static LoggerManager* GetInstance(){
            static LoggerManager instance;
            return &instance;
        }
        bool HasLogger(const std::string name){
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _table.find(name);
            if(it == _table.end()) { return false; }
            return true;
        }
        Logger::ptr GetLogger(const std::string& name){
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _table.find(name);
            if(it == _table.end()){
                return Logger::ptr();
            }
            return it->second;
        }
        void AddLogger(const std::string& name, Logger::ptr logger){
            std::unique_lock<std::mutex> lock(_mtx);
            _table.insert(std::make_pair(name, logger));
        }
        Logger::ptr RootLogger(){
            std::unique_lock<std::mutex> lock(_mtx);
            return _root;
        }
    };

    class GlobalLoggerBuilder : public Logger::Builder{
        public:
        using ptr = std::shared_ptr<GlobalLoggerBuilder>;
        Logger::ptr Build(){
            if(_name.empty()){
                std::cout << "日志器名不能为空!\n";
                abort();
            }
            if(_format.get() == nullptr){
                std::cout << "当前日志器：" << _name << " 未检测到日志格式，默认设置为[ %d{%H:%M:%S}%T%t%T[%p]%T[%c]%T%f:%l%T%m%n ]!\n";
                _format = std::make_shared<Formatter>();
            }
            if(_sinks.empty()){
                std::cout << "当前日志器：" << _name << " 未检测到落地方向，默认设置为标准输出!\n";
                _sinks.push_back(SkinFactory::Build<StdSink>());
            }
            if(LoggerManager::GetInstance()->HasLogger(_name) == true){ return LoggerManager::GetInstance()->GetLogger(_name); }
             Logger::ptr pl;
            if(_type == Logger::Type::LOGGER_SYNC){
                pl = std::make_shared<SyncLogger>(_name, _format, _sinks, _level);
            }else{
                pl = std::make_shared<ASyncLogger>(_name, _format, _sinks, _level);
            }
            LoggerManager::GetInstance()->AddLogger(_name, pl);
            return pl;
        }
    };
}